﻿using System;
using System.Linq;
using NLite;
using NLite.Threading;
using System.Threading;

namespace NLite.Messaging.Internal
{
    [Serializable]
    class Executor : IExecutor
    {
        public ISubject Subject { get; set; }
        public IMessageListenerManager ListnerManager { get; set; }
        public IDelegateInvoker DelegateInvoker { get; set; }

        public void Execute()
        {
            var handlers = Subject.Subscriber.Observers;
            if (handlers != null && handlers.Count > 0)
                DoExecute();
        }

       protected virtual void DoExecute()
        {
            if (Subject.Queue.Count == 0)
                return;

            var pair = Subject.Queue.Dequeue();
            var sender = pair.Key;
            var message = pair.Value as Message;

            var e = new MessageSendingEventArgs (sender,message);

            ListnerManager.OnSending(e);

            if (e.Canceled)
            {
                ListnerManager.OnSent(e);
                OnCompleted(message);
                return;
            }

            DoExecute(sender, message);
           
        }

       private static void OnCompleted(Message message)
       {
           message.IsCompleted = true;
       }

       private static void OnObserveExecuteCompleted(Message message)
       {
           var callback = message._data as ICallback;

           if (callback != null && callback.Delegate != null)
           {
               try
               {
                   callback.Delegate.DynamicInvoke(callback.Args);
               }
               catch (Exception ex)
               {
                   ex.Handle();
               }
           }
       }


        

        protected virtual void DoExecute(object sender, Message message)
        {
            var handlers = Subject.Subscriber.Observers.Select(i=>i.Key).ToArray();

            //if (ListnerManager.Where(item => (item.Type & MessageListnerType.Receiving) == MessageListnerType.Receiving).Count() == 0)
            {
                var barrier = new AtomicInteger(handlers.Length);
                Paraller.ForEach(handlers, handler => DoExecute(message, sender, handler, barrier));
                //foreach (var item in handlers)
                //{
                //    if (!DoExecute(message, sender, item.Key,barrier))
                //        break;
                //}
            }
            //else
            //{

            //    var syncHandlers = handlers.Where(i => i.Value == SubscribeMode.Sync).Select(i => i.Key).ToArray();
            //    var asyncHandlers = handlers.Where(i => i.Value == SubscribeMode.Async).Select(i => i.Key).ToArray();

            //    var handlerCount = syncHandlers.Length + asyncHandlers.Length;
            //    var barrier = new NLite.Threading.Barrier(handlerCount);

            //    foreach (var item in syncHandlers)
            //    {
            //        if (!DoExecute(message, sender, item,barrier))
            //            break;
            //    }


            //    Paraller.ForEach(asyncHandlers, handler => DoExecute(message, sender, handler,barrier));
            //}
        }

        protected bool DoExecute(Message message, object sender, IObserverHandler<IMessage> handle, AtomicInteger barrier)
        {
            var e = new MessageReceivingEventArgs (sender,message, handle.Target);
            try
            {
                ListnerManager.OnReceiving(e);
                if (!e.Ignored)
                {
                    message.results.Add(DelegateInvoker.Invoke<IMessage>(handle, sender, message));
                   
                    OnObserveExecuteCompleted(message);
                }

                message.IsHandled |= !e.Ignored;


                barrier--;

                if (barrier == 0)
                {
                    ListnerManager.OnSent(e);
                    OnCompleted(message);
                }
                return true;
            }
            catch (Exception ex)
            {
                message.IsHandled = false;
                var re = new MessageExceptionEventArgs (sender, message,ex);
                ListnerManager.OnReceivingException(re);
                ex.Handle();
                return !re.Canceled; 
            }
            finally
            {
                ListnerManager.OnReceived(new MessageEventArgs (sender, message));
                e = null;
            }
        }

    }
}
